# symdown.py: Markdown editor for S60 Ed.3 smartphones
# -*- coding: utf-8 -*-
# 
# Copyright (C) Dmitri Brechalov, 2010
#
# Project URL: http://code.google.com/p/symdown/
# 
# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the Free
# Software Foundation.
# 
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
# more details.

import appuifw2
import e32
import key_codes
import os
import sys
import re

#### in case the program runs as a script
sys.path.append('C:\\Python')
sys.path.append('E:\\Python')

from utils import *
from xtext import xText

UID = u"e3e34da5"
VERSION = '0.2'
TITLE = 'SymDown'

ParaChar = u"\u2029"

class MarkdownEditor(xText):
    def __init__(self):
        xText.__init__(self)
        self.title = u(TITLE)
        self.version = VERSION
        self.editor.style = appuifw2.STYLE_BOLD
#         self.editor.font = 'normal'

    def setExitKeyText(self):
        txt = u('Exit')
        appuifw2.app.exit_key_text = txt
        self.bindExitKey((u('Insert'), self.insertMarkup),
                         (u('Character'), self.insertEntity))

    def moveMenu(self):
        self.rebindFunKeys()
        ans = appuifw2.popup_menu([
                u('Section'),
                u('Top'),
                u('Bottom'),
                u('Goto line')])
        if ans is not None:
            if ans == 0:
                self.listHeadings()
            elif ans == 1:
                schedule(self.moveCursor, 0, appuifw2.EFNoMovement)
            elif ans == 2:
                schedule(self.moveCursor, len(self.editor.get()), appuifw2.EFNoMovement)
            elif ans == 3:
                schedule(self.gotoLine)
        self.moveEvent()
        
    def markup(self, markup, prompt=''):
        (pos, anchor, text) = self.editor.get_selection()
        if not text:
            if prompt:
                text = appuifw2.query(u(prompt), 'text')
                if text is None: return
            else:
                text = ''
        if markup.find('%s') > 0:
            text = u(markup) % (text)
        else:
            text = u(markup)
        if pos > anchor:
            pos, anchor = anchor, pos
        self.editor.delete(pos, anchor-pos)
        self.editor.set_pos(pos)
        self.editor.add(text)

    def insertCustomMarkup(self):
        text = appuifw2.query(u('Text to insert:'), 'text')
        if text is None: return
        self.markup(s(text))

    def insertMarkup(self):
        markup = (
            # (Label,            Markup)
            ('Paragraph',       '\n\n'),
            ('Line break',       '  \n'),
            ('Bold',            '**%s**'),
            ('Italic',          '_%s_'),
            ('Code',            '`%s`'),
            ('Header 1',        '# %s'),
            ('Header 2',        '## %s'),
            ('Header 3',        '### %s'),
            ('Header 4',        '#### %s'),
            ('Header 5',        '##### %s'),
            ('Header 6',        '###### %s'),
            ('Blockquote',      '> %s'),
            ('List item',       '* '),
            ('Preformated',     '    %s'),
            ('Horizontal rule', '\n---\n'),
            ('Link',            '[%s]()'),
            ('Auto link',       '<%s>'),
            ('Image',           '![](%s)'),
#             ('Custom...',       self.insertCustomMarkup),
            )
        self.insertFromList(markup)

    def insertEntity(self):
        characters = (
            # (Label,            Markup)
            ('\\', r'\\'),
            ('`', r'\`'),
            ('*', r'\*'),
            ('_', r'\_'),
            ('{', r'\{'),
            ('}', r'\}'),
            ('[', r'\['),
            (']', r'\]'),
            ('(', r'\('),
            (')', r'\)'),
            ('#', r'\#'),
            ('+', r'\+'),
            ('-', r'\-'),
            ('.', r'\.'),
            ('!', r'\!'),
            )
        self.insertFromList(characters)

    def insertFromList(self, markup):
        labels = map(lambda x: u(x[0]), markup)
        data = map(lambda x: x[1], markup)
        ans = appuifw2.selection_list(labels, 1)
        if ans is None: return
        selected = data[ans]
        if callable(selected):
            selected()
        else:
            self.markup(selected)
        
    def insertNewlineAndIndent(self):
        def getCurrentLine():
            pos = self.editor.get_pos()
            txt = self.editor.get()[:pos]
            # look for the start of line
            i = pos-1
            while i >= 0 and txt[i] != ParaChar:
                i -= 1
            return txt[i+1:]
        line = getCurrentLine()
        indentchars = u''
        mo = re.match(u'^([>* ]+ )', line)
        if mo is not None:
                indentchars += mo.group(1)
        self.editor.add(ParaChar+indentchars)

    def clickEvent(self):
        self.insertNewlineAndIndent()

    def getFont(self):
        appuifw2.query(u("Font: %s Style: %s" % (str(self.editor.font), str(self.editor.style))), 'query', ok=u('OK'), cancel=u(''))

    def listHeadings(self):
        ln = list()
        lp = list()
        txt = self.editor.get()
        count = 1
        for mo in re.finditer(u'(^|\u2029)(#+)(.+?)#*($|\u2029)', txt):
            n = len(mo.group(2)) - 1
            hdr = u'  ' * n + mo.group(3).strip()
            ln.append(hdr)
            lp.append(mo.start())
            count += 1
        ans = appuifw2.selection_list(ln, 1)
        if ans is None: return
        pos = lp[ans]
        self.editor.set_pos(pos+1)

    def cxapeligu(self):
        '''Redonas la saman tekston kun ĉapeligitajn literojn el x-skribo
        '''
        unikoda = ('Ĉ','ĉ','Ĝ','ĝ','Ĥ','ĥ','Ĵ','ĵ','Ŝ','ŝ','Ŭ','ŭ')
        xskriba = ('Cx','cx','Gx','gx','Hx','hx','Jx','jx','Sx','sx','Ux','ux')
        txt = self.editor.get()
        for i in range(len(unikoda)):
            txt = txt.replace(u(xskriba[i]), u(unikoda[i]))
        pos = self.editor.get_pos()
        self.editor.set(txt)
        self.editor.select_all()
        if self.editor.can_copy():
            self.editor.copy()
        self.editor.clear_selection()
        try:
            self.editor.set_pos(pos)
        except:
            self.editor.set_pos(0)

    def aboutDlg(self):
        appuifw2.query(u('%s\nVersion %s\n(C) Dmitri Brechalov, 2010' % (TITLE, self.version)), 'query', ok=u(''), cancel=u('Close'))

    def quit(self):
        if self.notSaved():
            if not self.fileSave(): return
        self.app_lock.signal()
        if appuifw2.app.uid() == UID:
            appuifw2.app.set_exit() # running as app

    def run(self):
        self.app_lock = e32.Ao_lock()
        appuifw2.app.menu = [
#             (u('Get Font'), self.getFont),
            (u("File"), ((u("Open"), self.fileOpen),
                         (u("Save"), self.fileSave),
                         (u("Save As"), self.fileSaveAs),
                         (u("New"), self.fileNew),)),
            (u('Insert'), ((u('Markdown'), self.insertMarkup),
                           (u('Escaped Character'), self.insertEntity),)),
            (u("Edit"), ((u("Cut"), self.cut),
                         (u("Copy"), self.copy),
                         (u("Paste"), self.paste),
                         (u("Select All"), self.selectAll),
                         (u("Select None"), self.selectNone),)),
            (u("Search"), ((u("Find Forward"), self.findTextForward),
                           (u("Find Backward"), self.findTextBackward),
                           (u("Replace"), self.replaceText),
                           (u("Move"), self.moveMenu))),
            (u("About"), self.aboutDlg),
            (u("Exit"), self.quit)
            ]
        self.setExitKeyText()
        self.bindSelectKey(self.clickEvent)
        self.bindYesKey(self.cxapeligu)
        self.editor.has_changed = False
        e32.ao_yield()
        appuifw2.app.body = self.editor

        old_exit_key_text = appuifw2.app.exit_key_text
        old_menu_key_text = appuifw2.app.menu_key_text
        appuifw2.app.menu_key_text = u("Options")
        self.rebindFunKeys()
        self.app_lock.wait()
        appuifw2.app.exit_key_text = old_exit_key_text

if __name__ == '__main__':
    editor = MarkdownEditor()
    editor.run()
